package com.sharp.portal.controller;

import com.github.pagehelper.PageInfo;
import com.sharp.authorization.holder.TokenHolder;
import com.sharp.framework.frame.cache.base.FrameCacheTypeConst;
import com.sharp.framework.service.cache.FrameCacheService;
import com.sharp.framework.service.callback.MyCallBack;
import com.sharp.framework.utils.HttpUtil;
import com.sharp.kernel.config.response.Response;
import com.sharp.kernel.config.response.ResponseHandler;
import com.sharp.kernel.constant.CommonConst;
import com.sharp.kernel.exception.ServiceException;
import com.sharp.kernel.mybatis.mapper.MysqlBaseMapper;
import com.sharp.kernel.utils.BeanUtil;
import com.sharp.kernel.utils.DbUtil;
import com.sharp.kernel.utils.JsonUtil;
import com.sharp.kernel.utils.ReflectionUtil;
import com.sharp.kernel.vo.common.CommonResVO;
import com.sharp.kernel.vo.common.DeleteCommonReqVO;
import com.sharp.kernel.vo.query.QueryReqVO;
import com.sharp.kernel.vo.query.QueryRespVO;
import com.sharp.portal.repository.PortalRepository;
import com.sharp.portal.service.PortalAspectService;
import com.sharp.portal.service.PortalManageService;
import com.sharp.portal.service.PortalQueryService;
import com.sharp.portal.service.PortalUploadService;
import com.sharp.portal.service.excel.PortalExcelService;
import com.sharp.portal.service.impl.PortalAspectServiceImpl;
import com.sharp.portal.utils.PortalUtil;
import com.sharp.portal.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Title: BaseController
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2020/2/12 14:32
 */

@Slf4j
@SuppressWarnings("unchecked")
public abstract class BasePortalController<SERVICE extends PortalRepository<ENTITY, DAO>,
        DAO extends MysqlBaseMapper<ENTITY>, ENTITY> extends PortalCommonController {

    private static final int SERVICE_INDEX = 0;
    private static final int DAO_INDEX = 1;
    private static final int ENTITY_INDEX = 2;
    private final Class<SERVICE> repositoryClazz = (Class<SERVICE>) ReflectionUtil.getSuperClassGenericType(
            this.getClass(), SERVICE_INDEX);
    private final Class<ENTITY> entityClazz = (Class<ENTITY>) ReflectionUtil.getSuperClassGenericType(this.getClass(),
            ENTITY_INDEX);
    @Resource
    private PortalUploadService portalUploadService;
    @Resource
    private PortalQueryService portalQueryService;
    @Resource
    private FrameCacheService frameCacheService;
    @Resource
    private PortalAspectServiceImpl portalAspectServiceImpl;

    @Resource
    private PortalManageService portalManageService;

    @Resource
    private PortalExcelService portalExcelService;

    @ApiIgnore
    @RequestMapping(value = "/model", method = RequestMethod.GET)
    public ResponseEntity<Response<ModelDisplayVO>> getModelDisplay(HttpServletRequest request) {
        String uri = getCurrentUri(request);
        ModelDisplayVO vo = portalManageService.getModelDisplay(uri);
        return ResponseHandler.generateResponse(vo);
    }

    @ApiIgnore
    @RequestMapping(value = "/layout", method = RequestMethod.GET)
    public ResponseEntity<Response<LinkedHashMap<String, FieldDisplayVO>>> getFieldDisplay(HttpServletRequest request) {
        LinkedHashMap<String, FieldDisplayVO> map = getFieldDisplayMap(request);
        return ResponseHandler.generateResponse(map);
    }

    private LinkedHashMap<String, FieldDisplayVO> getFieldDisplayMap(HttpServletRequest request) {
        String uri = getCurrentUri(request);
        return portalManageService.getFieldDisplayMap(uri);
    }

    @ApiIgnore
    @RequestMapping(value = "/id", method = RequestMethod.GET)
    public ResponseEntity<Response<QueryRespVO<Map<String, String>>>> getById(String id) {
        ENTITY data = getRepository().selectByPrimaryKey(id);
        PageInfo pageInfo = new PageInfo();
        pageInfo.getList().add(data);
        QueryRespVO<Map<String, String>> res = QueryRespVO.buildMap(pageInfo);
        return ResponseHandler.generateResponse(res);
    }

    protected SERVICE getRepository() {
        return BeanUtil.getBean(repositoryClazz);
    }

    @ApiIgnore
    @RequestMapping(value = "", method = RequestMethod.GET)
    public ResponseEntity<Response<QueryRespVO<Map<String, String>>>> getAll(QueryReqVO req) {
        PageInfo<ENTITY> res = portalQueryService.queryAll(req, getRepository(), entityClazz);
        return ResponseHandler.generateResponse(QueryRespVO.buildMap(res));
    }

    @ApiIgnore
    @RequestMapping(value = "/query", method = RequestMethod.POST)
    public ResponseEntity<Response<QueryRespVO<Map<String, String>>>> query(HttpServletRequest request,
                                                                            @RequestBody QueryConditionReq req) {
        PageInfo<?> pageInfo = DbUtil.buildPageInfo(req);
        if (CollectionUtils.isNotEmpty(req.getConditionList())) {
            req.getConditionList().addAll(fillCondition(request));
        } else {
            req.setConditionList(fillCondition(request));
        }
        getAspectService().queryBefore(req);
        PageInfo<ENTITY> pageInfoRes = portalQueryService.query(req, getRepository(), entityClazz, pageInfo);
        QueryRespVO<Map<String, String>> res = QueryRespVO.buildMap(getAspectService().queryAfter(pageInfoRes));
        return ResponseHandler.generateResponse(res);
    }

    protected List<ConditionVO> fillCondition(HttpServletRequest request) {
        return PortalUtil.fillCondition(request);
    }

    protected PortalAspectService getAspectService() {
        return portalAspectServiceImpl;
    }

    @ApiIgnore
    @RequestMapping(value = "/export", method = RequestMethod.POST)
    public void export(HttpServletRequest request, HttpServletResponse response, @RequestBody QueryConditionReq req) {
        List<ENTITY> data = portalQueryService.query(req, getRepository(), entityClazz);
        LinkedHashMap<String, FieldDisplayVO> fieldDisplayMap = getFieldDisplayMap(request);
        List<LinkedHashMap<String, String>> excelData = portalQueryService.dataDisplayConvert(data, fieldDisplayMap);
        List<LinkedHashMap<String, String>> res = getAspectService().exportAfter(excelData);
        HttpUtil.attachmentExcel(request, response, new MyCallBack() {
            @Override
            public void onSuccess(Object... param) {
                Map<String, List<LinkedHashMap<String, String>>> excelMap = new LinkedHashMap<>();
                excelMap.put("sheet1", res);
                portalExcelService.generateExcel(excelMap, fieldDisplayMap, (OutputStream) param[0]);
            }
        });
    }

    @ApiIgnore
    @RequestMapping(value = "/tree", method = RequestMethod.GET)
    public ResponseEntity<Response<List<TreeDisplayVO>>> getTree(HttpServletRequest request) {
        String uri = getCurrentUri(request);
        PageInfo<ENTITY> dataList = portalQueryService.getAllValid(getRepository(), entityClazz, null);
        List<Map<String, String>> data = JsonUtil.readJson(dataList, List.class, Map.class, String.class, String.class);
        List<TreeDisplayVO> tree = portalManageService.getTreeDisplay(uri, data);
        return ResponseHandler.generateResponse(tree);
    }

    @ApiIgnore
    @RequestMapping(value = "", method = RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<Response<CommonResVO>> update(@RequestBody Map<String, String> req) {
        ENTITY entity = ReflectionUtil.copy(req, entityClazz);
        getAspectService().updateBefore(entity);
        int number;
        try {
            number = getRepository().updateByPrimaryKeySelective(entity);
        } catch (Exception e) {
            throw new ServiceException("", e);
        }
        getAspectService().updateAfter(entity);
        String result = number == 0 ? CommonConst.FAILED : CommonConst.SUCCESS;
        return ResponseHandler.generateResponse(new CommonResVO(result, number));
    }

    @ApiIgnore
    @RequestMapping(value = "", method = RequestMethod.PUT)
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<Response<CommonResVO>> add(@RequestBody Map<String, String> req) {
        ENTITY entity = JsonUtil.readJson(req, entityClazz);
        getAspectService().addBefore(entity);
        int number;
        try {
            number = getRepository().insertSelective(entity);
        } catch (Exception e) {
            throw new ServiceException("", e);
        }
        getAspectService().addAfter(entity);
        String result = number == 0 ? CommonConst.FAILED : CommonConst.SUCCESS;
        return ResponseHandler.commonResponse(result, number);
    }

    @ApiIgnore
    @RequestMapping(value = "", method = RequestMethod.DELETE)
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<Response<CommonResVO>> delete(DeleteCommonReqVO req) {
        int number;
        if (StringUtils.equals(req.getAbsolute(), CommonConst.YES)) {
            getAspectService().deleteBefore(req.getId());
            number = getRepository().deleteByPrimaryKey(req.getId());
            getAspectService().deleteAfter(req.getId());
        } else {
            getAspectService().disableBefore(req.getId());
            number = getRepository().disableByPrimaryKey(req.getId());
            getAspectService().disableAfter(req.getId());
        }
        String result = number == 0 ? CommonConst.FAILED : CommonConst.SUCCESS;
        return ResponseHandler.commonResponse(result, number);
    }

    @ApiIgnore
    @RequestMapping(value = "/refresh", method = RequestMethod.POST)
    public ResponseEntity<Response<CommonResVO>> refresh() {
        getRepository().refreshCache();
        for (FrameCacheTypeConst cacheType : FrameCacheTypeConst.values()) {
            frameCacheService.refresh(cacheType.name());
        }
        return ResponseHandler.commonResponse();
    }

    @ApiIgnore
    @RequestMapping(value = "/template/export", method = RequestMethod.GET)
    public void getTemplate(HttpServletRequest request, HttpServletResponse response) {
        LinkedHashMap<String, FieldDisplayVO> fieldDisplayMap = getFieldDisplayMap(request);
        HttpUtil.attachmentExcel(request, response, new MyCallBack() {
            @Override
            public void onSuccess(Object... param) {
                Map<String, List<LinkedHashMap<String, String>>> excelMap = new LinkedHashMap<>();
                excelMap.put("sheet1", null);
                portalExcelService.generateExcel(excelMap, fieldDisplayMap, (OutputStream) param[0]);
            }
        });
    }

    @ApiIgnore
    @RequestMapping(value = "/upload/progress", method = RequestMethod.GET)
    public ResponseEntity<Response<CommonResVO>> uploadProgress() {
        String res = portalUploadService.getUploadProgress(entityClazz, getToken());
        return ResponseHandler.commonResponse(res);
    }

    protected String getToken() {
        return TokenHolder.get();
    }

    @ApiIgnore
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public ResponseEntity<Response<CommonResVO>> uploadExcel(HttpServletRequest request, MultipartFile file) {
        LinkedHashMap<String, FieldDisplayVO> map = getFieldDisplayMap(request);
        try {
            File tempFile = File.createTempFile("tmp", null);
            file.transferTo(tempFile);
            portalUploadService.uploadExcel(tempFile, getRepository(), map, entityClazz, getToken());
        } catch (IOException e) {
            log.error("", e);
        }
        return ResponseHandler.commonResponse();
    }
}
